const { Op } = require("sequelize");
const { Role, sequelize } = require("../../../../models");
const utils = require("../../../../utils");
const roleMenuService = require("../../role_menu");
const menuService = require("../../menu/service/MenuServiceImpl");
module.exports = {
  // 创建/编辑角色
  async saveOrUpdate(req, res, next) {
    try {
      const { id, parentId } = req.body;
      // 校验parentId是否存在

      // parentId需存在到数据库中
      if (parentId) {
        const parentMenu = await Role.findByPk(parentId);
        if (!parentMenu) {
          throw new Error("上级角色不存在");
        }
      }
      return id
        ? this.updateData(req, res, next)
        : this.saveData(req, res, next);
    } catch (error) {
      next(error);
    }
  },

  // 创建角色
  async saveData(req, res, next) {
    try {
      const user = await Role.create(req.body);
      return user ? res.cc(null, "成功创建角色") : res.err("创建角色失败");
    } catch (error) {
      next(error);
    }
  },

  // 更新角色
  async updateData(req, res, next) {
    try {
      // 1、校验角色是否存在
      let dataItem = await this.checkMenuInfo(req, res, next);
      if (!dataItem) return;
      // 2、组装参数
      await utils.assignEditValue(dataItem, req.body);
      dataItem.parentId = req.body.parentId ?? null; // 不传时默认为null

      // 3、执行更新
      const result = await dataItem.save();
      return result ? res.cc(null, "更新成功") : res.err("更新失败");
    } catch (error) {
      next(error);
    }
  },

  // 获取角色树
  async getTree(req, res, next) {
    try {
      const roleTree = await Role.findAll({
        order: [["createTime", "DESC"]],
      });
      if (!roleTree) return res.err("未获取到角色树");

      const dataValuesArr = roleTree.map((item) => item.get({ plain: true }));

      // 根据parentId,id组成树结构,下级用children字段接收
      const treeData = await utils.arrToTree(dataValuesArr);
      // 返回树结构
      return res.cc(treeData, "成功获取角色树");
    } catch (error) {
      next(error);
    }
  },

  // 删除角色(必须先删除下级角色)
  async deleteWithoutSub(req, res, next) {
    try {
      const { id } = req.body;
      if (!id) return res.err("请输入角色id");

      const dataItem = await Role.findByPk(id);
      if (!dataItem) {
        return res.err("角色不存在");
      }

      const treeIdList = await this.getTreeIdList(req, res, next);
      if (treeIdList.length > 1) {
        return res.err("请先删除子角色");
      }

      const result = await dataItem.destroy();
      return result ? res.cc(null, "删除成功") : res.err("删除失败");
    } catch (error) {
      next(error);
    }
  },

  // 删除角色(会直接删除所有下级角色)
  async deleteAllSub(req, res, next) {
    try {
      const { id } = req.body;
      if (!id) return res.err("请输入角色id");
      const dataItem = await Role.findByPk(id);
      if (!dataItem) {
        return res.err("角色不存在");
      }
      const treeIdList = await this.getTreeIdList(req, res, next);
      // 删除所有子角色
      const result = await Role.destroy({
        where: {
          id: {
            [Op.in]: treeIdList,
          },
        },
      });
      return result ? res.cc(null, "删除成功") : res.err("删除失败");
    } catch (error) {
      next(error);
    }
  },
  // 校验perms字段格式
  checkPerms(perms) {
    if (!perms) {
      throw new Error("perms字段不能为空");
    }
    if (Object.prototype.toString.call(perms) !== "[object Object]") {
      throw new Error("perms必须是对象格式");
    }
    const permsKeys = Object.keys(perms);
    for (const key of permsKeys) {
      if (Object.prototype.toString.call(perms[key]) !== "[object Array]") {
        throw new Error("perms内部数据必须是数组格式");
      }
    }
  },

  // 绑定菜单
  async bindMenu(req, res, next) {
    try {
      await sequelize.transaction(async (t) => {
        const { id, perms } = req.body;

        // 1、判断角色id是否存在
        await this.checkRoleId(id);
        // 2、校验perms字段格式
        await this.checkPerms(perms);
        // 3、获取perms内部的menuIds和btnIds
        let menuIds = [],
          btnIds = [];
        for (const key of Object.keys(perms)) {
          menuIds.push(key);
          btnIds = btnIds.concat(perms[key]);
        }
        // 4、校验菜单id是否合法
        await roleMenuService.checkMenuIds(menuIds);

        // 5、校验按钮id是否合法
        await menuService.checkBtnIds(btnIds);

        // 6、关联角色和菜单
        await roleMenuService.bindRoleMenu({ id, perms }, t);

        // 7、返回结果
        res.cc(null, "绑定成功");
      });
    } catch (error) {
      next(error);
    }
  },

  // 获取角色菜单
  async getRoleMenu(req, res, next) {
    try {
      const { id } = req.body;
      await this.checkRoleId(id);

      const menuIds = await roleMenuService.getRoleMenuIds(id);
      return res.cc(menuIds, "获取成功");
    } catch (error) {
      next(error);
    }
  },

  // 校验角色信息
  async checkRoleId(id) {
    if (!id) {
      throw new Error("请输入角色id");
    }
    // 判断id的数据是否存在
    const dataItem = await Role.findByPk(id);
    if (!dataItem) {
      throw new Error("角色不存在");
    }
    return dataItem;
  },

  //  校验角色是否存在、编辑时更改信息
  async checkMenuInfo(req, res, next) {
    try {
      const { id, parentId } = req.body;

      // 判断id的数据是否存在
      const dataItem = await this.checkRoleId(id);

      const treeIdList = await this.getTreeIdList(req, res, next);
      if (treeIdList.includes(parentId)) {
        throw new Error("角色不能作为自己或下级的子角色");
      }
      return dataItem;
    } catch (error) {
      next(error);
    }
  },

  // 获取角色列表
  async getTreeIdList(req, res, next) {
    try {
      const { id } = req.body;

      if (!id) return res.err("请输入角色id");

      const allIdList = await Role.findAll({
        attributes: ["id", "parentId"],
      });
      if (allIdList.length === 0) return res.cc(null, "角色不存在");
      // 查询树所有子角色id
      return await utils.getAllTreeIdByRecursion(allIdList, req.body.id, [id]);
    } catch (error) {
      next(error);
    }
  },
};
